//
//  XHHTTPService.m
//  XHNetwork
//
//  Created by xinph on 2020/3/11.
//  Copyright © 2020 xinph. All rights reserved.
//

#import "XHHTTPService.h"
#import "XHHTTPServiceConstant.h"
#import "XHHTTPResponse.h"
#import "AFNetworkReachabilityManager.h"

// 是否为空对象
#define XHObjectIsNil(__object)  ((nil == __object) || [__object isKindOfClass:[NSNull class]])

// 字符串为空
#define XHStringIsEmpty(__string) ((__string.length == 0) || XHObjectIsNil(__string))

// 字符串不为空
#define XHStringIsNotEmpty(__string)  (!XHStringIsEmpty(__string))

// 输出日志 (格式: [时间] [哪个方法] [哪行] [输出内容])
#ifdef DEBUG
#define XHNSLog(format, ...)  printf("\n[%s] %s\n", __TIME__, [[NSString stringWithFormat:format, ##__VA_ARGS__] UTF8String]);
#else

#define XHNSLog(format, ...)

#endif

/**
 * 知识点
 //- (RACSignal *)replayLast 就是用Capacity为1的RACReplaySubject来替换- (RACSignal *)replay的`subject。
 其作用是使后来订阅者只收到最后的历史值。
 //- (RACSignal *)replayLazily和- (RACSignal *)replay的区别就是replayLazily会在第一次订阅的时候才订阅sourceSignal。
 //  replay、replayLast、replayLazily的区别 ReactiveCocoa提供了这三个简便的方法允许多个订阅者订阅一个信号，却不会重复执行订阅代码，并且能给新加的订阅者提供订阅前的值。
 replay和replayLast使信号变成热信号，且会提供所有值(-replay) 或者最新的值(-replayLast) 给订阅者。
 replayLazily 会提供所有的值给订阅者 replayLazily还是冷信号 避免了冷信号的副作用
 *
 */
/// The Http request error domain
NSString *const XHHTTPServiceErrorDomain = @"XHHTTPServiceErrorDomain";
/// 请求成功，但statusCode != 0
NSString *const XHHTTPServiceErrorResponseCodeKey = @"XHHTTPServiceErrorResponseCodeKey";
NSString * const XHHTTPServiceErrorRequestURLKey = @"XHHTTPServiceErrorRequestURLKey";
NSString * const XHHTTPServiceErrorHTTPStatusCodeKey = @"XHHTTPServiceErrorHTTPStatusCodeKey";
NSString * const XHHTTPServiceErrorDescriptionKey = @"XHHTTPServiceErrorDescriptionKey";
NSString * const XHHTTPServiceErrorMessagesKey = @"XHHTTPServiceErrorMessagesKey";

/// 连接服务器失败 default
NSInteger const XHHTTPServiceErrorConnectionFailed = 668;
NSInteger const XHHTTPServiceErrorJSONParsingFailed = 669;

NSInteger const XHHTTPServiceErrorBadRequest = 670;
NSInteger const XHHTTPServiceErrorRequestForbidden = 671;
/// 服务器请求失败
NSInteger const XHHTTPServiceErrorServiceRequestFailed = 672;
NSInteger const XHHTTPServiceErrorSecureConnectionFailed = 673;


@interface XHHTTPService ()

/**
 The network reachability manager. `AFURLSessionManager` uses the `sharedManager` by default.
 */
@property (readwrite, nonatomic, strong) AFNetworkReachabilityManager *reachabilityManager;

@end
@implementation XHHTTPService

#pragma mark -  HTTPService
static id service_ = nil;

+(instancetype) sharedInstance {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        service_ = [self new];
    });
    return service_;
}

+ (id)allocWithZone:(struct _NSZone *)zone{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        service_ = [super allocWithZone:zone];
    });
    return service_;
}

- (id)copyWithZone:(NSZone *)zone {
    return service_;
}

- (instancetype)init {
    self = [super init];
    //开启网络监控
    [self startNetworkMonitor];
    return self;
}

/// 开启网络监控
- (void)startNetworkMonitor {
    self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
    /// 开启网络监测
    [self.reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
        
        if (status == AFNetworkReachabilityStatusUnknown) {
            //            [JDStatusBarNotification showWithStatus:@"网络状态未知" styleName:JDStatusBarStyleWarning];
            //            [JDStatusBarNotification showActivityIndicator:YES indicatorStyle:UIActivityIndicatorViewStyleWhite];
            NSLog(@"--- 未知网络 ---");
        }else if (status == AFNetworkReachabilityStatusNotReachable) {
            //            [JDStatusBarNotification showWithStatus:@"网络不给力，请检查网络" styleName:JDStatusBarStyleWarning];
            //            [JDStatusBarNotification showActivityIndicator:YES indicatorStyle:UIActivityIndicatorViewStyleWhite];
            NSLog(@"--- 无网络 ---");
        }else{
            NSLog(@"--- 有网络 ---");
            //            [JDStatusBarNotification dismiss];
        }
    }];
    [self.reachabilityManager startMonitoring];
}

#pragma mark - Request
- (RACSignal *)enqueueRequest:(XHHTTPRequest *)request resultClass:(Class /*subclass of MHObject*/) resultClass{
    /// request 必须的有值
    if (!request || XHStringIsEmpty([YTKNetworkConfig sharedConfig].baseUrl)) return [RACSignal error:[NSError errorWithDomain:XHHTTPServiceErrorDomain code:-1 userInfo:nil]];
    /// 发起请求
    /// concat:按一定顺序拼接信号，当多个信号发出的时候，有顺序的接收信号。 这里传进去的参数，不是parameters而是之前通过
    /// urlParametersWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters;穿进去的参数
    RACSignal *signal = [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
        [request startWithCompletionBlockWithSuccess:^(__kindof YTKBaseRequest * _Nonnull request) {
            //log
            [self HTTPRequestLog:request];
            /// CoderMikeHe Fixed : 这里需要上层模块根据实际业务来解析数据模型
            ///
            /// =============== 上层模块数据解析 =============
            ///
            /// 打包成元祖 回调数据
            [subscriber sendNext:RACTuplePack(request.response , request.responseObject)];
            [subscriber sendCompleted];
        } failure:^(__kindof YTKBaseRequest * _Nonnull request) {
            //log
            [self HTTPRequestLog:request];
            NSError *error = [self _errorFromRequestWithRequest:request];
            [subscriber sendError:error];
        }];
        
        return [RACDisposable disposableWithBlock:^{
            [request stop];
        }];
        
    }] setNameWithFormat:@"%@ -rac_%@: %@, parameters: %@, constructingBodyWithBlock:", self.class, request.urlParameters.method, request.urlParameters.path, request.urlParameters.parameters];
    /// replayLazily:replayLazily会在第一次订阅的时候才订阅sourceSignal
    /// 会提供所有的值给订阅者 replayLazily还是冷信号 避免了冷信号的副作用
    return [[signal
             replayLazily]
            setNameWithFormat:@"-enqueueRequestWithPath: %@ parameters: %@ method: %@", request.urlParameters.path, request.urlParameters.parameters , request.urlParameters.method];
}

#pragma mark - Error Handling
/// 请求错误解析
- (NSError *)_errorFromRequestWithRequest:(YTKBaseRequest * _Nonnull)request {
    /// 不一定有值，则HttpCode = 0;
    NSInteger HTTPCode = request.response.statusCode;
    NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
    /// default errorCode is MHHTTPServiceErrorConnectionFailed，意味着连接不上服务器
    NSInteger errorCode = XHHTTPServiceErrorConnectionFailed;
    NSString *errorDesc = @"服务器出错了，请稍后重试~";
    /// 其实这里需要处理后台数据错误，一般包在 request.responseObject
    /// HttpCode错误码解析 https://www.guhei.net/post/jb1153
    /// 1xx : 请求消息 [100  102]
    /// 2xx : 请求成功 [200  206]
    /// 3xx : 请求重定向[300  307]
    /// 4xx : 请求错误  [400  417] 、[422 426] 、449、451
    /// 5xx 、600: 服务器错误 [500 510] 、600
    NSInteger httpFirstCode = HTTPCode/100;
    if (httpFirstCode>0) {
        if (httpFirstCode==4) {
            /// 请求出错了，请稍后重试
            if (HTTPCode == 408) {
#if defined(DEBUG)||defined(_DEBUG)
                errorDesc = @"请求超时，请稍后再试(408)~"; /// 调试模式
#else
                errorDesc = @"请求超时，请稍后再试~";      /// 发布模式
#endif
            }else{
#if defined(DEBUG)||defined(_DEBUG)
                errorDesc = [NSString stringWithFormat:@"请求出错了，请稍后重试(%zd)~",HTTPCode];                   /// 调试模式
#else
                errorDesc = @"请求出错了，请稍后重试~";      /// 发布模式
#endif
            }
        }else if (httpFirstCode == 5 || httpFirstCode == 6){
            /// 服务器出错了，请稍后重试
#if defined(DEBUG)||defined(_DEBUG)
            errorDesc = [NSString stringWithFormat:@"服务器出错了，请稍后重试(%zd)~",HTTPCode];                      /// 调试模式
#else
            errorDesc = @"服务器出错了，请稍后重试~";       /// 发布模式
#endif
            
        }else if (!self.reachabilityManager.isReachable){
            /// 网络不给力，请检查网络
            errorDesc = @"网络开小差了，请稍后重试~";
        }
    }else{
        if (!self.reachabilityManager.isReachable){
            /// 网络不给力，请检查网络
            errorDesc = @"网络开小差了，请稍后重试~";
        }
    }
    switch (HTTPCode) {
        case 400:{
            errorCode = XHHTTPServiceErrorBadRequest;           /// 请求失败
            break;
        }
        case 403:{
            errorCode = XHHTTPServiceErrorRequestForbidden;     /// 服务器拒绝请求
            break;
        }
        case 422:{
            errorCode = XHHTTPServiceErrorServiceRequestFailed; /// 请求出错
            break;
        }
        default:
            /// 从error中解析
            if ([request.error.domain isEqual:NSURLErrorDomain]) {
#if defined(DEBUG)||defined(_DEBUG)
                errorDesc = [NSString stringWithFormat:@"请求出错了，请稍后重试(%zd)~",request.error.code];                   /// 调试模式
#else
                errorDesc = @"请求出错了，请稍后重试~";        /// 发布模式
#endif
                switch (request.error.code) {
                    case NSURLErrorSecureConnectionFailed:
                    case NSURLErrorServerCertificateHasBadDate:
                    case NSURLErrorServerCertificateHasUnknownRoot:
                    case NSURLErrorServerCertificateUntrusted:
                    case NSURLErrorServerCertificateNotYetValid:
                    case NSURLErrorClientCertificateRejected:
                    case NSURLErrorClientCertificateRequired:
                        errorCode = XHHTTPServiceErrorSecureConnectionFailed; /// 建立安全连接出错了
                        break;
                    case NSURLErrorTimedOut:{
#if defined(DEBUG)||defined(_DEBUG)
                        errorDesc = @"请求超时，请稍后再试(-1001)~"; /// 调试模式
#else
                        errorDesc = @"请求超时，请稍后再试~";        /// 发布模式
#endif
                        break;
                    }
                    case NSURLErrorNotConnectedToInternet:{
#if defined(DEBUG)||defined(_DEBUG)
                        errorDesc = @"网络开小差了，请稍后重试(-1009)~"; /// 调试模式
#else
                        errorDesc = @"网络开小差了，请稍后重试~";        /// 发布模式
#endif
                        break;
                    }
                }
            }
    }
    userInfo[XHHTTPServiceErrorHTTPStatusCodeKey] = @(HTTPCode);
    userInfo[XHHTTPServiceErrorDescriptionKey] = errorDesc;
    if (request.requestTask.currentRequest.URL != nil) userInfo[XHHTTPServiceErrorRequestURLKey] = request.requestTask.currentRequest.URL.absoluteString;
    if (request.requestTask.error != nil) userInfo[NSUnderlyingErrorKey] = request.requestTask.error;
    return [NSError errorWithDomain:XHHTTPServiceErrorDomain code:errorCode userInfo:userInfo];
}

#pragma mark - 打印请求日志
- (void)HTTPRequestLog:(YTKBaseRequest * _Nonnull)request {
    XHNSLog(@">>>>>>>>>>>>>>>>>>>>>👇 REQUEST FINISH 👇>>>>>>>>>>>>>>>>>>>>>>>>>>");
    XHNSLog(@"Request%@=======>:%@", request.error?@"失败":@"成功", request.requestTask.currentRequest.URL.absoluteString);
    XHNSLog(@"requstHeader=====>:%@", request.requestTask.currentRequest.allHTTPHeaderFields);
    if (!XHObjectIsNil(request.requestArgument)) {
        XHNSLog(@"requestBody======>:%@", request.requestArgument);
    }
//    XHNSLog(@"response=========>:%@", request.requestTask.response);
    if (!XHObjectIsNil(request.error)) {
           XHNSLog(@"error============>:%@", request.error);
       }
    if (XHStringIsNotEmpty(request.responseString)) {
        XHNSLog(@"responseBody======>:%@", request.responseString);
    }
    XHNSLog(@"<<<<<<<<<<<<<<<<<<<<<👆 REQUEST FINISH 👆<<<<<<<<<<<<<<<<<<<<<<<<<<");
}

@end

